#include <iostream>
#include <string>
#include <unordered_map>
#include<cstring>
#include <sys/types.h>
#include <arpa/inet.h>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "Log.hpp"
#include"InetAddr.hpp"
using std::cout;
using std::endl;
const static int DefaultSocket = -1;

class Chat_server
{
public:
    Chat_server(uint16_t port) : _port(port), _socketfd(DefaultSocket)
    {}

    void InitServer()
    {
        _socketfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_socketfd < 0)
        {
            LOG(FATAL, "socketfd failed");
            exit(1);
        }
        LOG(DEBUG, "socketfd success");
        struct sockaddr_in server;
        bzero(&server, sizeof(server));
        server.sin_port = htons(_port);
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;

        int n = bind(_socketfd, (struct sockaddr *)&server, sizeof(server));
        if (n < 0)
        {
            LOG(FATAL, "bind failed");
            exit(1);
        }
        LOG(DEBUG, "bind success");
    }
    std::string AddUser(char *buffer, int n, sockaddr_in client)
    {
        std::string name(buffer + 5, buffer + n);
        _hash.insert({name,client});//client是从网络中来的序列
        LOG(INFO,"add user %s",name.c_str());
        return name;
    }

    std::string Who(struct sockaddr_in client)
    {
        for(auto&e:_hash)
        {
            if(strcmp(inet_ntoa(client.sin_addr),inet_ntoa(e.second.sin_addr))==0)
            return e.first;
        }
        return "Unknown person";
    }
    void StartServer()
    {
        LOG(DEBUG,"StartServer");
        while (true)
        {
            char buffer[1024];
            sockaddr_in client;
            socklen_t len = sizeof(client);
            ssize_t n = recvfrom(_socketfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client, &len);
            if (n > 0)
            {
                buffer[n] = 0;
                std::string sign(buffer, buffer + 5);
                if (sign == "name:")
                {
                    std::string addInfo="add user:";
                    std::string name=AddUser(buffer, n, client);
                    addInfo+=name;
                    for(auto&e:_hash)
                    {
                        if(e.first!=name)
                        {
                            sendto(_socketfd,addInfo.c_str(),addInfo.size(),0,(struct sockaddr*)&e.second,sizeof(struct sockaddr_in));
                        }
                        else
                        {
                            // std::string tmp="#                               connect success,you are user";
                            std::string tmp="*                                          connect success,you are user";
                            tmp+=std::to_string(_hash.size());
                            sendto(_socketfd,tmp.c_str(),tmp.size(),0,(struct sockaddr*)&e.second,sizeof(struct sockaddr_in));
                        }
                    }
                    for(auto&e:_hash)cout<<e.first<<' ';
                    cout<<endl;
                    continue;
                }
                else
                {
                    std::string IP=inet_ntoa(client.sin_addr);
                    std::string what=Who(client)+" say: "+std::string(buffer,buffer+n);
                    for(auto&e:_hash)
                    {
                        std::string ip=inet_ntoa(e.second.sin_addr);
                        if(ip!=IP)
                        {
                            sendto(_socketfd,what.c_str(),what.size(),0,(struct sockaddr*)&e.second,sizeof(struct sockaddr_in));
                        }
                        else
                        {
                            std::string tmp="*                                          send success";
                            sendto(_socketfd,tmp.c_str(),tmp.size(),0,(struct sockaddr*)&e.second,sizeof(struct sockaddr_in));
                        }
                    }
                }
            }
        }
    }

    ~Chat_server() {}

private:
    uint16_t _port;
    int _socketfd;
    std::unordered_map< std::string,struct sockaddr_in> _hash; // Name,ip+port
};